﻿using FluentParser.Expressions;
using FluentParser.Expressions.Terminals.Parsing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FluentParser.Tests.Expressions.Terminals.Parsing
{
    [TestClass]
    public class ParseFilterExpressionTerminalTests
    {
        [TestMethod]
        public void Parse_WhenParsingInvalidStringButNoMinimum_ShouldReturnEmptyString()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, null, null);
            ParsingResult<string> result = expr.Parse("1234567890");

            string expected = "";
            string actual = result.Value;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(0, result.Length);
        }

        [TestMethod]
        public void Parse_WhenParsingInvalidStringButMinimumOfOne_ShouldReturnNull()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, 1, null);

            var expected = (ParsingResult<string>)null;
            var actual = expr.Parse("1234567890");

            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Parse_WhenParsingLongWordWithALimitOfFive_ShouldReturnFirstFiveCharacters()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, null, 5);
            ParsingResult<string> result = expr.Parse("Someverylongword");

            string expected = "Somev";
            string actual = result.Value;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(expected.Length, result.Length);
        }

        [TestMethod]
        public void Parse_WhenParsingValidFirstCharacterButNotOtherCharacter_ShouldOnlyReturnFirstCharacter()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMajuscule, null, null);
            ParsingResult<string> result = expr.Parse("Someverylongword");

            string expected = "S";
            string actual = result.Value;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(expected.Length, result.Length);
        }

        [TestMethod]
        public void ParseNode_WhenParsingInvalidStringButNoMinimum_ShouldReturnEmptyString()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, null, null);
            ParsingNodeResult node = expr.ParseNode("1234567890");

            string expected = "";
            string actual = node.Value as string;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(0, node.Length);
            Assert.AreEqual(1, node.ExploredLength);
        }

        [TestMethod]
        public void ParseNode_WhenParsingInvalidStringButMinimumOfOne_ShouldReturnNull()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, 1, null);
            ParsingNodeResult node = expr.ParseNode("1234567890");

            string expected = null;
            string actual = node.Value as string;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(0, node.Length);
            Assert.AreEqual(1, node.ExploredLength);
        }

        [TestMethod]
        public void ParseNode_WhenParsingLongWordWithALimitOfFive_ShouldReturnFirstFiveCharacters()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMinuscule, null, 5);
            ParsingNodeResult node = expr.ParseNode("Someverylongword");

            string expected = "Somev";
            string actual = node.Value as string;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(5, node.Length);
            Assert.AreEqual(5, node.ExploredLength);
        }

        [TestMethod]
        public void ParseNode_WhenParsingValidFirstCharacterButNotOtherCharacter_ShouldOnlyReturnFirstCharacter()
        {
            ParseFilterExpressionTerminal expr = new ParseFilterExpressionTerminal(IsAlpha, IsMajuscule, null, null);
            ParsingNodeResult node = expr.ParseNode("Someverylongword");

            string expected = "S";
            string actual = node.Value as string;

            Assert.AreEqual(expected, actual);
            Assert.AreEqual(1, node.Length);
            Assert.AreEqual(2, node.ExploredLength);
        }

        #region Private methods

        private static bool IsAlphaNumeric(char chr)
        {
            return IsAlpha(chr) || IsNumber(chr);
        }

        private static bool IsAlpha(char chr)
        {
            return IsMinuscule(chr) || IsMajuscule(chr);
        }

        private static bool IsMinuscule(char chr)
        {
            return chr >= 'a' && chr <= 'z';
        }

        private static bool IsMajuscule(char chr)
        {
            return chr >= 'A' && chr <= 'Z';
        }

        private static bool IsNumber(char chr)
        {
            return chr >= '0' && chr <= '9';
        }

        #endregion Private methods
    }
}
